﻿using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Apex.Commons;
using Apex.DomainCommons.Models;

namespace MediatR
{
    /// <summary>
    /// 包含对 MediatR 的扩展方法
    /// </summary>
    public static class MediatorExtensions
    {
        /// <summary>
        /// 把assemblies及直接或者间接引用的程序集（排除系统程序集）中的MediatR 相关类进行注册
        /// </summary>
        /// <param name="services"></param>
        /// <param name="assemblies">程序集</param>
        /// <returns></returns>
        public static IServiceCollection AddMediatR(this IServiceCollection services,IEnumerable<Assembly> assemblies)
        {
            return services.AddMediatR(assemblies.ToArray());
        }

        /*
         *  用于从 DbContext 中获取领域事件，并将其发布到 MediatR 处理程序
         *  首先，它通过检查 ChangeTracker 中的实体来找到所有发生变化的领域实体，这些实体实现了 IDomainEvents 接口并且有待发布的领域事件
         *  然后，它从每个领域实体中获取领域事件，并将其添加到列表中
         *  接着，它清空每个领域实体的领域事件列表，以避免重复发布
         *  最后，它通过 MediatR 的 Publish 方法将每个领域事件发布到相应的处理程序
         */

        /// <summary>
        /// 分发领域事件至 MediatR 处理程序
        /// </summary>
        /// <param name="mediator">Mediator 实例</param>
        /// <param name="ctx">DbContext 实例</param>
        /// <returns>异步任务。</returns>
        public static async Task DispatchDomainEventsAsync(this IMediator mediator, DbContext ctx)
        {
            // 获取所有变化的领域实体，这些实体实现了 IDomainEvents 接口，并且有领域事件需要发布
            var domainEntities = ctx.ChangeTracker
                .Entries<IDomainEvents>()
                .Where(x => x.Entity.GetDomainEvents().Any());

            // 从每个领域实体中获取领域事件并添加到列表中
            var domainEvents = domainEntities
                .SelectMany(x => x.Entity.GetDomainEvents())
                .ToList(); // 立即加载列表，否则在 ClearDomainEvents() 后会丢失

            // 清空领域实体的领域事件列表
            domainEntities.ToList()
                .ForEach(entity => entity.Entity.ClearDomainEvents());

            // 发布每个领域事件至 MediatR 处理程序
            foreach (var domainEvent in domainEvents)
            {
                await mediator.Publish(domainEvent);
            }
        }
    }
}
